#ifndef _TCPSERVER_
#define _TCPSERVER_

#include "ThreadPool/log.hpp"
#include "ThreadPool/ThreadPool.hpp"
#include "ThreadPool/Task.hpp"
#include <cstdio>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>


const static int gbacklog = 20; // 一般不能设置太大和太小 -后面再说

static void handleTheClient(int serviceSock, const std::string clientIp, uint16_t clientPort)
{
    char buffer[1024];
    while (true)
    {
        // 接收的话注意是字节流，所以以前的文件那一套可以使用的
        ssize_t n = recv(serviceSock, buffer, sizeof(buffer) - 1, 0); // 阻塞等待，没有from就是适合面向字节流的，即TCP的，但是UDP写可以用的
        if (n > 0)
        {
            buffer[n] = '\0';
            printf("[%s: %d]# ", clientIp.c_str(), clientPort);
            std::cout << buffer << std::endl;

            // 然后发送
            // 可以使用以前文件操作那一套或者send
            // send(serviceSock, buffer, sizeof(buffer) - 1, 0);  // 阻塞发送，使用处理套接字
            ssize_t s = write(serviceSock, buffer, strlen(buffer));
            if (s < 0)
            {
                logMessage(ERROR, "发送信息失败！%d-%s (file:%s func:%s line:%d)", errno, strerror(errno), __FILE__, __func__, __LINE__);
                break;
            }
        }
        else if (n == 0)
        {
            // 对方关闭了连接，这里也进行关闭
            printf("[%s: %d]客户端关闭连接,我也关闭此连接\n", clientIp.c_str(), clientPort);
            close(serviceSock);
            break;
        }
        else
        {
            // 小于零说明接收失败
            logMessage(ERROR, "接收信息失败！%d-%s (file:%s func:%s line:%d)", errno, strerror(errno), __FILE__, __func__, __LINE__);
            close(serviceSock);
            break;
        }
    }
}

class TCPServer;

class TCPThreadData
{
public:
    TCPServer* _obj;
    std::string _ip;
    uint16_t _port;

    TCPThreadData(TCPServer* obj, std::string& ip, uint16_t port)
    :_obj(obj), _ip(ip), _port(port)
    {}
};

class TCPServer
{
private:
    static void* threadTest(void* argc)
    {
        // 线程先自我分离
        pthread_detach(pthread_self());
        TCPThreadData* data = (TCPThreadData*)argc;
        // 注意多线程里面不可关闭监听套接字哦，此时都是在同一个进程下的，属于共享哦
        handleTheClient(data->_obj->_service_sock, data->_ip, data->_port);
    }

public:
    TCPServer(uint16_t port, std::string ip = "") : _server_ip(ip), _server_port(port),
                                                    _listen_sock(-1), _service_sock(-1)
    {
    }

    void initTCPServer()
    {
        // 初始化TCP服务器
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0); // 流式套接 自动识别为TCP协议，面向字节流
        if (_listen_sock < 0)
        {
            logMessage(FATAL, "套接字创建失败！%d-%s (file:%s func:%s line:%d)", errno, strerror(errno), __FILE__, __func__, __LINE__);
            exit(1);
        }

        // 服务器将自己的ip和端口绑定到对应的套接字上。
        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof server_addr); // 初始化为0
        server_addr.sin_family = AF_INET;            // 家族还是网络IPV4
        server_addr.sin_port = htons(_server_port);  // 转化为网络字节序的端口号
        if (_server_ip.empty())
            server_addr.sin_addr.s_addr = INADDR_ANY;  // 如果是空字符串就如此处理
        else
        {
            int n = inet_aton(_server_ip.c_str(), &server_addr.sin_addr); // 直接写入结构中，将点分式的ip地址转化为数字然后转化为网络字节序
            if (n == 0)
            {
                logMessage(FATAL, "写入ip地址无效！%d-%s (file:%s func:%s line:%d)", errno, strerror(errno), __FILE__, __func__, __LINE__);
                exit(2);
            }
        }

        // bind
        if (bind(_listen_sock, (struct sockaddr *)&server_addr, sizeof server_addr) < 0)
        {
            logMessage(FATAL, "服务器端ip与port与套接字绑定失败！%d-%s (file:%s func:%s line:%d)", errno, strerror(errno), __FILE__, __func__, __LINE__);
            exit(3);
        }
        logMessage(NORMAL, "绑定成功！......");
        // UDP到这一步初始化完毕，但是TCP还存在一步，需要进行连接
        // 因为TCP是面向连接的，我们正式通信前需要先建立连接
        // 此时就相当于设置_sock套接字为监听模式了
        if (listen(_listen_sock, gbacklog) < 0)
        {
            logMessage(FATAL, "连接失败！%d-%s (file:%s func:%s line:%d)", errno, strerror(errno), __FILE__, __func__, __LINE__);
            exit(4);
        }
    }

    // 初始化完就是启动了
    void start()
    {
        // 服务器先接收消息，然后在发送消息
        // TCP协议能否简单的像UDP那样直接进行通信吗？显然不能，在连接阶段使用的套接字是监听套接字，对信息处理并且发送是处理套接字所要干的事情
        struct sockaddr_in clientAddr;
        socklen_t clientAddrLen = sizeof clientAddr; // 用来接收客户端信息的套接字结构体
        bool flag = true;
        while (true)
        {
            if (flag)
            {
                flag = false;
                printf("--请选择线程池执行多少个线程个数：");
                int n = 0;
                std::cin >> n;
                ThreadPool<Task>::getThreadPoolObject(n)->run();
                logMessage(NORMAL, "线程池开始运行...... 线程个数：%d", n);
            }

            // 首先确保常驻
            // 首先获取连接，连接我返回，不连接在这里进行阻塞
            _service_sock = accept(_listen_sock, (struct sockaddr *)&clientAddr, &clientAddrLen);
            if (_service_sock < 0)
            {
                logMessage(ERROR, "连接客户端失败,重新连接... %d-%s (file:%s func:%s line:%d)", errno, strerror(errno), __FILE__, __func__, __LINE__);
                continue;
                // 连接失败不是致命的错误，重连一次即可
            }
            // 现在使用堆客户端信息处理的代码即可，这里我将他们封装为一个函数
            // 首先在外面先获取客户端的ip和端口
            std::string clientIp = inet_ntoa(clientAddr.sin_addr); // 将网络字节序的网络地址ip转化为点分十进制的字符串
            uint16_t clientPort = ntohs(clientAddr.sin_port);      // 网络转为本地字节序，注意是16位整数
            logMessage(NORMAL, "[%s: %d]客户端连接成功.....", clientIp.c_str(), clientPort);

            // 处理信息 -单进程版本1.0，只能等当前客户端处理完才能轮到下一个客户端
            // handleTheClient(_service_sock, clientIp, clientPort);
            // 2.0 我们换成多进程版本
            // signal(SIGCHLD, SIG_IGN);  // 17号信号置为忽略 - 应用层的忽略  这样子进程退出发送此信号父进程指定忽略 不是系统层面上的忽略哦
            // pid_t pid = fork();
            // if (pid == 0)
            // {
            //     // 子进程
            //     close(_listen_sock);  // 子进程关闭监听套接字
            //     handleTheClient(_service_sock, clientIp, clientPort);
            //     exit(0);
            // }
            // // 父进程
            // close(_service_sock);  // 父进程关闭自己的处理套接字-连接套接字
            // 2.1 多进程版本我们利用孤儿进程进行托管，不用父进程进行维护
            // pid_t pid = fork();
            // if (pid == 0)
            // {
            //     // 子进程
            //     if (fork() > 0) exit(0);  // 子进程瞬间退出
            //     // 孙子进程
            //     handleTheClient(_service_sock, clientIp, clientPort);
            //     exit(0);
            // }
            // wait(nullptr);

            // 3.0 多线程版本  存在线程安全问题
            // pthread_t id;
            // TCPThreadData* data = new TCPThreadData(this, clientIp, clientPort);
            // pthread_create(&id, nullptr, threadTest, (void*)data);

            // 3.1 线程池版 不断接收任务-派发线程去执行任务即可
            ThreadPool<Task>::getThreadPoolObject()->pushTask(Task(_service_sock, clientIp, clientPort, handleTheClient));
        }

        close(_listen_sock);  // 服务器退出常驻进程，回收套接字资源
    }

    ~TCPServer()
    {}

private:
    std::string _server_ip;
    uint16_t _server_port;
    int _listen_sock;  // 监听套接字
    int _service_sock; // 处理套接字
};

#endif